
#include "../common/mem.h"
#include "../common/error.h"

#include  <stdlib.h>
#include  <stdio.h>
#include  <assert.h>
#include  <memory.h>

#include  "ptrlinkedlist.h"

typedef  struct _PtrLinkedListCell
{
	void* data;
	struct _PtrLinkedListCell* next;
}  PtrLinkedListCell;


PtrLinkedList* PtrLinkedList__alloc()
{
	PtrLinkedList* list = (PtrLinkedList*) mem_alloc(1, sizeof(PtrLinkedList));
	validate_allocation(list, "a Linked List");
	PtrLinkedList__init(list);

	return list;
}

void PtrLinkedList__init(PtrLinkedList* list)
{
	memset(list, 0, sizeof(PtrLinkedList));

	list->head = NULL; /*  not necessary here   */
}

void PtrLinkedList__free(PtrLinkedList* list)
{
	PtrLinkedList__clear(list);
	mem_free(list);
}

void PtrLinkedList__clear(PtrLinkedList* list)
{
	PtrLinkedListCellPtr cell, next;

	cell = list->head;

	while (cell != 0)
	{
		next = cell->next;
		free(cell);
		cell = next;
	}

	memset(list, 0, sizeof(PtrLinkedList));
}

PtrLinkedListCellPtr PtrLinkedList__make_cell(void * data)
{
	PtrLinkedListCellPtr tmp;

	tmp = (PtrLinkedListCellPtr) mem_alloc_and_zero(1, sizeof(PtrLinkedListCell));
	validate_allocation(tmp, "Internal linked-list structure");
	tmp->data = data;

	return tmp;
}

void PtrLinkedList__link_cell(PtrLinkedList* list,
							  PtrLinkedListCellPtr prev_cell,
							  PtrLinkedListCellPtr new_cell)
{
	if (prev_cell != 0)
	{
		new_cell->next = prev_cell->next;
		prev_cell->next = new_cell;
	}

	if (prev_cell == 0)
		list->head = new_cell;

	if (new_cell->next == 0)
		list->tail = new_cell;

	list->size++;
}

void PtrLinkedList__insert_head(PtrLinkedList* list, void* data)
{
	PtrLinkedListCellPtr tmp = PtrLinkedList__make_cell(data);
	tmp->next = list->head;
	PtrLinkedList__link_cell(list, 0, tmp);
}

void PtrLinkedList__insert_tail(PtrLinkedList* list, void* data)
{
	PtrLinkedListCellPtr tmp = PtrLinkedList__make_cell(data);
	PtrLinkedList__link_cell(list, list->tail, tmp);
}


void PtrLinkedList__unlink(PtrLinkedList* list, PtrLinkedListCellPtr cell,
						   PtrLinkedListCellPtr prev_cell)
{
	if (prev_cell != 0)
		prev_cell->next = cell->next;
	else
		list->head = cell->next;

	if (cell->next == 0)
		list->tail = prev_cell;

	mem_free(cell);

	list->size--;
}

int PtrLinkedList__delete(PtrLinkedList* list, void* data)
{
	PtrLinkedListCellPtr cell = list->head;
	PtrLinkedListCellPtr prev_cell = 0;

	while (cell != 0)
	{
		if (cell->data == data)
		{
			PtrLinkedList__unlink(list, cell, prev_cell);
			return 1;
		}

		prev_cell = cell;
		cell = cell->next;
	}

	return 0;
}


int PtrLinkedList__HasItems(const PtrLinkedList* list)
{
	return (PtrLinkedList__Count(list) != 0 ? 1 : 0);
}

int PtrLinkedList__Count(const PtrLinkedList* list)
{
	return list->size;
}

void* PtrLinkedList__pop_head(PtrLinkedList* list)
{
	void* result = 0;

	if (PtrLinkedList__HasItems(list))
	{
		PtrLinkedListCellPtr head = list->head;
		result = head->data;
		PtrLinkedList__unlink(list, head, 0);
	}

	return result;
}

void PtrLinkedList__iterator_init(PtrLinkedListIterator* iter,
								  PtrLinkedList* lst)
{
	*iter = lst->head;
}

void PtrLinkedList__iterator_next(PtrLinkedListIterator * iter )
{
	*iter = (*iter)->next;
}

int PtrLinkedList__iterator_is_done(const PtrLinkedListIterator iter)
{
	return (iter == NULL ? 1 : 0);
}

void* PtrLinkedList__iterator_get_data(const PtrLinkedListIterator iter)
{
	return iter->data;
}

